]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
Add app introspection API
authorJonas Ådahl <jadahl@gmail.com>
Wed, 5 Sep 2018 09:15:30 +0000 (11:15 +0200)
committerOlivier Fourdan <ofourdan@redhat.com>
Wed, 9 Jan 2019 09:13:45 +0000 (10:13 +0100)
Add a D-Bus API that allows the API user to introspect the application
state of the shell. Currently the only exposed information is list of
running applications and which one is active (i.e. has focus).

https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/326

data/dbus-interfaces/meson.build
data/dbus-interfaces/org.gnome.Shell.Introspect.xml [new file with mode: 0644]
data/gnome-shell-dbus-interfaces.gresource.xml
data/org.gnome.shell.gschema.xml.in
js/js-resources.gresource.xml
js/misc/introspect.js [new file with mode: 0644]
js/ui/main.js

index 4ba832661dfcd60e42125f4f7be545db3e34b493..c96bbbb4daa70d71042c086d083bf7394b4c0fec 100644 (file)
@@ -1,5 +1,6 @@
 dbus_interfaces = [
   'org.gnome.Shell.Extensions.xml',
+  'org.gnome.Shell.Introspect.xml',
   'org.gnome.Shell.PadOsd.xml',
   'org.gnome.Shell.Screencast.xml',
   'org.gnome.Shell.Screenshot.xml',
diff --git a/data/dbus-interfaces/org.gnome.Shell.Introspect.xml b/data/dbus-interfaces/org.gnome.Shell.Introspect.xml
new file mode 100644 (file)
index 0000000..10c48d6
--- /dev/null
@@ -0,0 +1,37 @@
+<!DOCTYPE node PUBLIC
+'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
+'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
+<node>
+
+  <!--
+      org.gnome.Shell.Introspect:
+      @short_description: Introspection interface
+
+      The interface used to introspect the state of Shell, such as running
+      applications, currently active application, etc.
+  -->
+  <interface name="org.gnome.Shell.Introspect">
+
+    <!--
+        RunningApplicationsChanged:
+        @short_description: Notifies when the running applications changes
+    -->
+    <signal name="RunningApplicationsChanged" />
+
+    <!--
+        GetRunningApplications:
+        @short_description: Retrieves the description of all running applications
+
+        Each application is associated by an application ID. The details of
+        each application consists of a varlist of keys and values. Available
+        keys are listed below.
+
+        'active-on-seats' - (as)   list of seats the application is active on
+                                   (a seat only has at most one active
+                                   application)
+    -->
+    <method name="GetRunningApplications">
+      <arg name="apps" direction="out" type="a{sa{sv}}" />
+    </method>
+  </interface>
+</node>
index 5183672c92daf67dfcbd2053b5b8a2867e7eaf87..ba148ca1f006462817f7f8443178334b768c11ba 100644 (file)
@@ -40,6 +40,7 @@
     <file preprocess="xml-stripblanks">org.gnome.SettingsDaemon.Wacom.xml</file>
     <file preprocess="xml-stripblanks">org.gnome.Shell.AudioDeviceSelection.xml</file>
     <file preprocess="xml-stripblanks">org.gnome.Shell.Extensions.xml</file>
+    <file preprocess="xml-stripblanks">org.gnome.Shell.Introspect.xml</file>
     <file preprocess="xml-stripblanks">org.gnome.Shell.HotplugSniffer.xml</file>
     <file preprocess="xml-stripblanks">org.gnome.Shell.PerfHelper.xml</file>
     <file preprocess="xml-stripblanks">org.gnome.Shell.PortalHelper.xml</file>
index a7251cd06c33994b207fc596f42400fe523d1617..87898a36e7f330e463d7868409179eedc95992e0 100644 (file)
         adapter is ever seen not to have devices associated to it.
       </description>
     </key>
+    <key name="introspect" type="b">
+      <default>false</default>
+      <summary>Enable introspection API</summary>
+      <description>
+        Enables a D-Bus API that allows to introspect the application state of
+        the shell.
+      </description>
+    </key>
     <child name="keybindings" schema="org.gnome.shell.keybindings"/>
     <child name="keyboard" schema="org.gnome.shell.keyboard"/>
   </schema>
index 75a6c3b124ef23eafa897281ce392c604874e771..a3dd2be08e41b8e03c03a764ecaed593455b8525 100644 (file)
@@ -16,6 +16,7 @@
     <file>misc/history.js</file>
     <file>misc/ibusManager.js</file>
     <file>misc/inputMethod.js</file>
+    <file>misc/introspect.js</file>
     <file>misc/jsParse.js</file>
     <file>misc/keyboardManager.js</file>
     <file>misc/loginManager.js</file>
diff --git a/js/misc/introspect.js b/js/misc/introspect.js
new file mode 100644 (file)
index 0000000..fa0a66e
--- /dev/null
@@ -0,0 +1,111 @@
+const Gio = imports.gi.Gio;
+const GLib = imports.gi.GLib;
+const Lang = imports.lang;
+const Meta = imports.gi.Meta;
+const Shell = imports.gi.Shell;
+
+const INTROSPECT_SCHEMA = 'org.gnome.shell';
+const INTROSPECT_KEY = 'introspect';
+const APP_WHITELIST = ['org.freedesktop.impl.portal.desktop.gtk'];
+
+const { loadInterfaceXML } = imports.misc.fileUtils;
+
+const IntrospectDBusIface = loadInterfaceXML('org.gnome.Shell.Introspect');
+
+var IntrospectService = new Lang.Class({
+    Name: 'IntrospectService',
+
+    _init() {
+        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(IntrospectDBusIface,
+                                                             this);
+        this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/Introspect');
+        Gio.DBus.session.own_name('org.gnome.Shell.Introspect',
+                                  Gio.BusNameOwnerFlags.REPLACE,
+                                  null, null);
+
+        this._runningApplications = {};
+        this._runningApplicationsDirty = true;
+        this._activeApplication = null;
+        this._activeApplicationDirty = true;
+
+        this._appSystem = Shell.AppSystem.get_default();
+        this._appSystem.connect('app-state-changed',
+                                () => {
+                                    this._runningApplicationsDirty = true;
+                                    this._syncRunningApplications();
+                                });
+
+        this._settings = new Gio.Settings({ schema_id: INTROSPECT_SCHEMA });
+
+        let tracker = Shell.WindowTracker.get_default();
+        tracker.connect('notify::focus-app',
+                        () => {
+                            this._activeApplicationDirty = true;
+                            this._syncRunningApplications();
+                        });
+
+        this._syncRunningApplications();
+    },
+
+    _isStandaloneApp(app) {
+        let windows = app.get_windows();
+
+        return app.get_windows().some(w => w.transient_for == null);
+    },
+
+    _isIntrospectEnabled() {
+       return this._settings.get_boolean(INTROSPECT_KEY);
+    },
+
+    _isSenderWhitelisted(sender) {
+       return APP_WHITELIST.includes(sender);
+    },
+
+    _syncRunningApplications() {
+        let tracker = Shell.WindowTracker.get_default();
+        let apps = this._appSystem.get_running();
+        let seatName = "seat0";
+        let newRunningApplications = {};
+
+        let newActiveApplication = null;
+        let focusedApp = tracker.focus_app;
+
+        for (let app of apps) {
+            let appInfo = {};
+            let isAppActive = (focusedApp == app);
+
+            if (!this._isStandaloneApp(app))
+                continue;
+
+            if (isAppActive) {
+                appInfo['active-on-seats'] = new GLib.Variant('as', [seatName]);
+                newActiveApplication = app.get_id();
+            }
+
+            newRunningApplications[app.get_id()] = appInfo;
+        }
+
+        if (this._runningApplicationsDirty ||
+            (this._activeApplicationDirty &&
+             this._activeApplication != newActiveApplication)) {
+            this._runningApplications = newRunningApplications;
+            this._activeApplication = newActiveApplication;
+
+            this._dbusImpl.emit_signal('RunningApplicationsChanged', null);
+        }
+        this._runningApplicationsDirty = false;
+        this._activeApplicationDirty = false;
+    },
+
+    GetRunningApplicationsAsync(params, invocation) {
+        if (!this._isIntrospectEnabled() &&
+            !this._isSenderWhitelisted(invocation.get_sender())) {
+            invocation.return_error_literal(Gio.DBusError,
+                                            Gio.DBusError.ACCESS_DENIED,
+                                            'App introspection not allowed');
+            return;
+        }
+
+        invocation.return_value(new GLib.Variant('(a{sa{sv}})', [this._runningApplications]));
+    }
+});
index 9e7ce8706fd56e21367327b6db9dc91680841263..f87b1ad021c9950cd4258464e22852b1fbd9a7ad 100644 (file)
@@ -20,6 +20,7 @@ const Environment = imports.ui.environment;
 const ExtensionSystem = imports.ui.extensionSystem;
 const ExtensionDownloader = imports.ui.extensionDownloader;
 const InputMethod = imports.misc.inputMethod;
+const Introspect = imports.misc.introspect;
 const Keyboard = imports.ui.keyboard;
 const MessageTray = imports.ui.messageTray;
 const ModalDialog = imports.ui.modalDialog;
@@ -82,6 +83,7 @@ var keyboard = null;
 var layoutManager = null;
 var kbdA11yDialog = null;
 var inputMethod = null;
+var introspectService = null;
 let _startDate;
 let _defaultCssStylesheet = null;
 let _cssStylesheet = null;
@@ -187,6 +189,8 @@ function _initializeUI() {
     windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
     componentManager = new Components.ComponentManager();
 
+    introspectService = new Introspect.IntrospectService();
+
     layoutManager.init();
     overview.init();